/**
 * @brief 实现线程池的封装
 * @details
 * 1.在线程的入口函数中创建eventloop,防止在线程创建和绑定eventloop之间产生未知的行为
 * 2.线程池采用RR轮转的方式进行线程的获取
 */
#pragma once

#include "eventLoop.hpp"

namespace hdp
{
    class LoopThread
    {
    private:
        /*用于实现_loop获取的同步关系，避免线程创建了,但是_loop还没有实例化之前去获取_loop*/
        std::mutex _mutex;             // 互斥锁
        std::condition_variable _cond; // 条件变量
        EventLoop *_loop;              // EventLoop指针变量，这个对象需要在线程内实例化
        std::thread _thread;           // EventLoop对应的线程

    private:
        /*实例化 EventLoop 对象,唤醒_cond上有可能阻塞的线程，并且开始运行EventLoop模块的功能*/
        void ThreadEntry()
        {
            EventLoop loop;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _loop = &loop;
                _cond.notify_all();
            }
            loop.Start();
        }

    public:
        /*创建线程，设定线程入口函数*/
        LoopThread() : _thread(std::thread(&LoopThread::ThreadEntry, this)), _loop(nullptr) {}
        /*返回当前线程关联的EventLoop对象指针*/
        EventLoop *GetEventLoop()
        {
            EventLoop *loop = nullptr;
            {
                std::unique_lock<std::mutex> lock(_mutex); // 加锁
                _cond.wait(lock, [&]()
                           { return _loop != nullptr; }); //_loop为nullptr就一直阻塞
                loop = _loop;
            }
            return loop;
        }
    };

    class LoopThreadPool
    {
    private:
        int _thread_count;                  // 从属线程的数量
        int _next_idx;                      // RR轮转的下标
        EventLoop *_base_loop;              // 主EventLoop,运行在主线程,从属线程数量为0,则所有的操作都在baseloop中运行
        std::vector<LoopThread *> _threads; // 保存所有的LoopThread对象
        std::vector<EventLoop *> _loops;    // 从属线程数量大于0则从_loops中进行线程EventLoop分配

    public:
        LoopThreadPool(EventLoop *baesloop)
            : _thread_count(0), _next_idx(0), _base_loop(baesloop) {}
        void SetThreadCount(const int &count) { _thread_count = count; }
        void Create()
        {
            if (_thread_count > 0)
            {
                _threads.resize(_thread_count);
                _loops.resize(_thread_count);
                for (int i = 0; i < _thread_count; i++)
                {
                    _threads[i] = new LoopThread();
                    _loops[i] = _threads[i]->GetEventLoop();
                }
            }
        }

        EventLoop *GetNextLoop()
        {
            if (_thread_count == 0)
            {
                return _base_loop;
            }
            _next_idx = (_next_idx + 1) % _thread_count;
            return _loops[_next_idx];
        }
    };
}